package jml.defaultimple.util;

import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Properties;

import jml.config.Config;
import jml.config.Descriptor;
import jml.config.FilterAtt;
import jml.config.Filters;
import jml.config.LabelGroups;
import jml.config.Plugin;
import jml.config.Plugins;
import jml.config.ReservedAtts;
import jml.def.Filter;
import jml.def.Label;
import jml.def.Library;
import jml.def.Title;
import jml.defaultimple.FilterImple;
import jml.defaultimple.LabelImple;
import jml.defaultimple.LibraryImple;
import jml.defaultimple.Sublabel;
import jml.defaultimple.TitleImple;
import jml.defaultimple.util.xml.XMLFactory;

public class JMLLoader
{
	public static Library load(String cfgFilename)
	{
		// leo el archivo de configuracion
		XMLFactory.load(cfgFilename);
		return _getLibrary();
	}

	private static Library _getLibrary()
	{
		Config config = Config.getConfig(true);

		jml.config.Library ulib = config.getLibrary();

		// la libreria tiene un conjunto de filters...
		LibraryImple library = new LibraryImple();

		// seteo el nombre de la libreria
		library.setName(ulib.getName());

		// seteo el nombre, path y extension de los descriptores
		String mediaFolders[] = ulib.getFolders();

		// seteo los filters en la libreria
		Filters f = config.getFilters();

		FilterImple filterImple;
		for(FilterAtt fa : f.getFilterAtts())
		{
			filterImple = new FilterImple();
			filterImple.setAlwaysShown(fa.isAlwaysShow());
			filterImple.setIfNotSpecified(fa.getIfNotSpecified());
			filterImple.setName(fa.getName());
			filterImple.setSort(fa.getSort());
			library.addFilter(filterImple);
		}

		// si hay plugins => agrego la solapa para plugins
		Plugins plugins = config.getPlugins();
		if(plugins != null)
		{
			filterImple = new FilterImple();
			filterImple.setAlwaysShown(plugins.isAlwaysShow());
			filterImple.setName(plugins.getFilterName());
			library.addFilter(filterImple);

			// agrego los labels para cada plugin
			for(Plugin p : plugins.getPlugins())
			{
				LabelImple lblPlu = new LabelImple();
				lblPlu.setName(p.getName());
				filterImple.addLabel(lblPlu);
				JMLPluginManager.getPlugin(p.getName()).setPluginLabel(lblPlu);
				JMLPluginManager.getPlugin(p.getName()).setPluginFilter(filterImple);
			}
		}

		// notifico a todos los plugins
		JMLPluginManager.notifyAll("beforeLoading", library);

		for(int i = 0; i < mediaFolders.length; i++)
		{
			// cargo todos los .jml (los descriptores)
			_load(library, mediaFolders[i]);
		}

		// ordeno las listas
		_sortTitles(library);

		// agrego los sublabels
		_procesarSublabels((LibraryImple) library);

		// ordeno los filters
		_sortFilters(library);

		// notifico a todos los plugins
		JMLPluginManager.notifyAll("afterLoading", library);

		return library;
	}

	private static void _sortFilters(LibraryImple library)
	{
		Filter[] filters = library.getFilters();

		for(int i = 0; i < filters.length; i++)
		{
			FilterImple filter = (FilterImple) filters[i];

			// no ordeno el filtro de los plugins, el orden lo determina el xml
			if(!filter.getName().equals(Config.getConfig(false).getPlugins().getFilterName()))
			{
				List<Label> labels = filter.getListLabels();
				Collections.sort(labels, new ComparatorImpleAZ());
			}
		}
	}

	private static void _load(LibraryImple library, String dir)
	{
		File f = new File(dir);

		Descriptor desc = Config.getConfig(false).getDescriptor();

		// obtengo la extension de los archivos descriptores
		String descriptorFileExtension = desc.getFileExtension();

		// obtengo solo los subdirectorios y el archivo descriptor
		String[] filenames = f.list(new FilenameFilter(descriptorFileExtension));

		for(String x : filenames)
		{
			String fullpath = f.getAbsolutePath() + "/" + x;
			File fr = new File(fullpath);
			if(fr.isFile())
			{
				// si es el descriptor entonces leo todos los datos
				_processJMLFile(fr, library);
			}
			else
			{	
				// si es un subdirectorio => proceso recursivo
				_load(library, fullpath);
			}
		}
	}

	private static void _processJMLFile(File f, LibraryImple library)
	{
		Config config = Config.getConfig(false);
		Descriptor desc = config.getDescriptor();

		String SEPARATOR = desc.getLabelSeparator();

		// leo el descriptor del album
		String attName;
		String attValue;
		Properties props = UFile.readProperties(f.getAbsolutePath());

		TitleImple title = new TitleImple();

		// -- ATRIBUTOS RESERVADOS --

		ReservedAtts ra = config.getReservedAtts();

		// id (id-att)
		attName = ra.getIdAtt();
		attValue = props.getProperty(attName);
		title.setId(attValue);

		// flg multiples albums
		attName = ra.getMultipleAlbumAtt();
		attValue = props.getProperty(attName);

		// si es un album multiple...
		if(attValue != null)
		{
			String subfolders[] = UString.split(attValue, SEPARATOR);
			for(int i = 0; i < subfolders.length; i++)
			{
				title.addSubfolder(subfolders[i]);
			}
		}

		// -- FILTROS --

		String defaultFilterValue = config.getFilters().getDefaultValue();

		Filter[] filters = library.getFilters();

		// itero filtro x filtro leyendo los labels definidos en props
		for(int i = 0; i < filters.length; i++)
		{
			// nombre del filtro (o nombre de la propiedad)
			attName = filters[i].getName();

			// valor/es definidos en el .jml
			attValue = props.getProperty(attName);

			// si NO es vacio o nulo lo seteo y pego la vuelta
			if(!UString.isNullOrEmpty(attValue))
			{
				// espliteo el string (varios generos, varios artistas, etc)
				String values[] = UString.split(attValue, SEPARATOR);

				for(int j = 0; j < values.length; j++)
				{
					String value = values[j].trim();
					// agrego el value en el tittle
					title.addLabel(filters[i].getName(), value);

					// agrego el label en el filter
					LabelImple lbl = _buscaYOAgregaLabel((FilterImple) filters[i], value);

					// agrego el tittle en el label del filter
					lbl.addTitle(title);

				}

				continue;
			}

			// si llegue aqui es porque el atributo es nulo o vacio
			// es decir: no esta definido o va por default...
			if(filters[i].getIfNotSpecified() == FilterAtt.SHOW_DEFAULT_VALUE)
			{
				attValue = defaultFilterValue;
				title.addLabel(filters[i].getName(), attValue);

				// agrego el label en el filter
				LabelImple lbl = _buscaYOAgregaLabel((FilterImple) filters[i], attValue);
				lbl.addTitle(title);
			}
		}

		// -- PATH DEL DISCO --
		title.setPath(f.getParent());

		// notifico a los plugins que se acaba de cargar un titulo
		JMLPluginManager.notifyAll("onLoad", title);
	}

	private static LabelImple _buscaYOAgregaLabel(FilterImple filter, String labelName)
	{
		LabelImple lbl = (LabelImple) filter.getLabel(labelName);
		if(lbl == null)
		{
			lbl = new LabelImple();
			lbl.setName(labelName);
			filter.addLabel(lbl);
		}

		return lbl;
	}

	private static void _sortTitles(Library library)
	{
		Filter filters[] = library.getFilters();

		// recorro los filtros
		for(int i = 0; i < filters.length; i++)
		{
			for(Label label : filters[i].getLabels())
			{
				LabelImple imple = (LabelImple) label;
				List<Title> lista = imple.getListTitles();
				String sort = filters[i].getSort();
				if(sort != null)
				{
					Collections.sort(lista, new TittleComparatorImple(sort));
				}
			}
		}
	}

	private static void _procesarSublabels(Library library)
	{
		// configuracion de groups
		LabelGroups labelGroups = Config.getConfig(false).getLabelGroups();

		// obtengo los filtros
		Filter[] filters = library.getFilters();

		for(Filter filter : filters)
		{
			_processFilter((FilterImple) filter, labelGroups);
		}
	}

	private static void _processFilter(FilterImple filter, LabelGroups labelGroups)
	{
		// primero agrego todos los underlabels y sus sublabels
		String underlabels[] = labelGroups.getUnderLabelsByFilter(filter.getName());

		if(underlabels == null)
		{
			return;
		}

		for(String underlabel : underlabels)
		{
			LabelImple label = _buscaYOAgrega(filter, underlabel);
			label.setName(underlabel);

			// sublabels
			Sublabel[] sublabels = labelGroups.getSublabels(filter.getName(), underlabel);
			for(Sublabel sublabel : sublabels)
			{
				// OJO CON ESTE IF

				label.addSublabel(sublabel.getName());
				if(!sublabel.isShowAsLabel())
				{
					filter.excludeLabel(sublabel.getName());
				}
			}
		}
	}

	private static LabelImple _buscaYOAgrega(FilterImple filter, String labelname)
	{
		LabelImple label = (LabelImple) filter.getLabel(labelname);

		if(label == null)
		{
			label = new LabelImple();
			label.setName(labelname);
			filter.addLabel(label);
		}

		return label;
	}

	static class ComparatorImpleAZ implements Comparator<Label>
	{
		@Override
		public int compare(Label o1, Label o2)
		{
			return o1.getName().compareTo(o2.getName());
		}

	}
}
